#include "dataBase_test.h"

//全局的搜索表
SearchList g_search_db;

/**
 * @brief: 搜索检索表中的学生 填充该学生的信息 返回该学生在检索表中的索引
 * @para: {lable, key, stu_data}
 * @return: index
 **/
int SearchList::searchStudent(std::string lable, std::string key, StuDataStruct& stu_data)
{
    int total = this->numofMap();
    for(int i = 0; i < this->numofMap(); i++)
    {
        if(key == this->readList(i, lable))
        {
            stu_data.id = this->readList(i, "id");
            stu_data.name = this->readList(i, "name");
            stu_data.password = this->readList(i, "password");
            stu_data.username = this->readList(i, "username");
            stu_data.major = this->readList(i, "major");
            stu_data.grade = this->readList(i, "grade");
            stu_data._class = this->readList(i, "class");
            LOG::logPrint("find student(id: " + stu_data.id + ")", LOG::MSG);
            return i;
        }
    }
    return -1;
}

/**
 * @brief: 更新检索表 将新建的学生加入检索表
 * @para: {new_stu_data}
 * @return: bool
 **/
bool SearchList::update(StuDataStruct new_stu_data)
{
    StuDataStruct temp_config;
    int index = this->searchStudent("id", new_stu_data.id, temp_config);
    if(index >= 0)
    {
        //如果需要更新的用户已存在
        this->writeList(index, "name", new_stu_data.name);
        this->writeList(index, "username", new_stu_data.username);
        this->writeList(index, "password", new_stu_data.password);
        this->writeList(index, "major", new_stu_data.major);
        this->writeList(index, "grade", new_stu_data.grade);
        this->writeList(index, "class", new_stu_data._class);
        LOG::logPrint("modifying student data in search list", LOG::MSG);
    }
    else
    {
        index = this->numofMap();
        this->writeList(index, "name", new_stu_data.name);
        this->writeList(index, "id", new_stu_data.id);
        this->writeList(index, "username", new_stu_data.username);
        this->writeList(index, "password", new_stu_data.password);
        this->writeList(index, "major", new_stu_data.major);
        this->writeList(index, "grade", new_stu_data.grade);
        this->writeList(index, "class", new_stu_data._class);
        LOG::logPrint("add student msg into search list", LOG::MSG);
    }
    return true;
}

/**
 * @msg: 设置成绩数据库班级数据文件路径
 * @param {std::string major, std::string grade, std::string _class} 
 * @return: void
 */
void SubjectConfig::setClass(std::string major, std::string grade, std::string _class)
{
    YamlHandler::loadFile("./data/scores/" + major + "/" + grade + "/" + _class + "/config.yaml");
    this->subjects = this->getTotalSubjects();
    LOG::logPrint("loading SubjectConfig file: " + YamlHandler::currentPath(), LOG::MSG);
}

/**
 * @msg: 设置成绩数据库班级数据文件路径
 *       用于直接从tcp字节流获取
 * @param {string msg} 
 * @return: int(索引)
 */
void SubjectConfig::setClass(std::string msg)
{
    this->setClass(peekString(msg, 1), peekString(msg, 2), peekString(msg, 3));
}

/**
 * @msg: 给数据库班级数据文件添加科目描述
 * @param {std::string subject, int maxscore, int passscore, std::string examedate} 
 * @return: void
 */
void SubjectConfig::addSubject(std::string subject, int maxscore, int passscore, std::string examedate)
{
    for(size_t i = 0; i < numofMap(); i++)
    {
        if(this->readList(i, "subject") == subject)
        {
            this->writeList(i, "maxscore", std::to_string(maxscore));
            this->writeList(i, "passscore", std::to_string(passscore));
            this->writeList(i, "examdate", examedate);
            this->subjects = this->getTotalSubjects();
            LOG::logPrint("modifying subject config.", LOG::MSG);
            return;
        }
    }
    //如果没有这门科目则新建
    this->writeList(this->numofMap(), "subject", subject);
    this->writeList(this->numofMap(), "maxscore", std::to_string(maxscore));
    this->writeList(this->numofMap(), "passscore", std::to_string(passscore));
    this->writeList(this->numofMap(), "examdate", examedate);
    //增加完一科更新一次
    this->subjects = this->getTotalSubjects();
    LOG::logPrint("adding subject config.", LOG::MSG);
}

/**
 * @msg: 给数据库班级数据文件添加科目描述
 *       用于直接从tcp字节流获取科目信息
 * @param {string msg} 
 * @return: void
 */
void SubjectConfig::addSubject(std::string msg)
{
    std::cout << msg << std::endl;
    int total = atoi(peekString(msg, 1).c_str());
    for(size_t i = 0; i < total; i++)
    {
        string subject = peekString(msg, i * 2 + 2);
        string examdate = peekString(msg, i * 2 + 3);
        this->addSubject(subject, 100, 60, examdate);
    }
}

/**
 * @brief: 从班级科目配置文件中获取所有科目信息
 * @para: {void}
 * @return: std::vector<SubjectScore>
 **/
std::vector<SubjectScore> SubjectConfig::getTotalSubjects()
{
    std::vector<SubjectScore> res_vector;
    int total = numofMap();
    for(size_t i = 0; i < total; i++)
    {
        SubjectScore temp;
        temp.subject = this->readList(i, "subject");
        temp.examdate = this->readList(i, "examdate");
        temp.score = -1;
        res_vector.insert(res_vector.end(), temp);
    }
    return res_vector;
}

/**
 * @brief: 将学生信息数据结构中的信息压缩到字节流
 *         用于发送个人信息
 * @para: {stu_data, option(true: 发送成绩数据 false: 发送个人信息)}
 * @return: string
 **/
string DataBaseRW::zipData(StuDataStruct stu_data)
{
    string res;
    res = stu_data.name + " " +
          stu_data.id + " " +
          stu_data._class + " " +
          stu_data.grade + " " +
          stu_data.major + " " +
          stu_data.phone + " " +
          stu_data.email + " " +
          stu_data.sex + " " +
          stu_data.birthday + " " +
          stu_data.pid + " " +
          stu_data.address + " " +
          stu_data.remark;
    return res;
}

/**
 * @brief: 将学生信息数据结构中的信息压缩到字节流
 *         用于发送成绩数据
 * @para: {stu_data, option(true: 发送成绩数据 false: 发送个人信息)}
 * @return: string
 **/
string DataBaseRW::zipScoreData(StuDataStruct stu_data)
{
    string res;
    //数据帧格式：科目数 + [科目 分数 日期]
    res = std::to_string(stu_data.scores.size()) + " ";
    for(size_t i = 0; i < stu_data.scores.size(); i++)
    {
        res = res + stu_data.scores[i].subject + " " +
                    std::to_string(stu_data.scores[i].score) + " " +
                    stu_data.scores[i].examdate + " ";
    }    
    return res;         
}

/**
 * @brief: 将班级科目数据结构中的信息压缩到字节流
 *         用于发送班级科目
 * @para: {stu_data, option(true: 发送成绩数据 false: 发送个人信息)}
 * @return: string
 **/
string DataBaseRW::zipSubjectData(std::vector<SubjectScore> subjects)
{
    string res;
    res = std::to_string(subjects.size()) + " ";
    for(size_t i = 0; i < subjects.size(); i++)
    {
        res = res + subjects[i].subject + " " +
                    subjects[i].examdate + " ";
    }    
    return res;
}

/**
 * @brief: 将接收到的信息解压缩 保存到学生信息数据结构中
 *         用于接收个人信息
 * @para: {msg}
 * @return: StuDataStruct
 **/
void DataBaseRW::unzipData(string msg, StuDataStruct &stu_data)
{
    stu_data.name = peekString(msg, 1);
    stu_data.id = peekString(msg, 2);
    stu_data._class = peekString(msg, 3);
    stu_data.grade = peekString(msg, 4);
    stu_data.major = peekString(msg, 5);
    stu_data.phone = peekString(msg, 6);
    stu_data.email = peekString(msg, 7);
    stu_data.sex = peekString(msg, 8);
    stu_data.birthday = peekString(msg, 9);
    stu_data.pid = peekString(msg, 10);
    stu_data.address = peekString(msg, 11);
    stu_data.remark = peekString(msg, 12);
}

/**
 * @brief: 将接收到的信息解压缩 保存到学生信息数据结构中
 *         用于接收成绩数据
 * @para: {msg}
 * @return: StuDataStruct
 **/
void DataBaseRW::unzipScoreData(string msg, StuDataStruct &stu_data)
{
    int total = atoi(peekString(msg, 1).c_str());
    if(stu_data.scores.size() != total)
    {
        LOG::logPrint("num is no capable", LOG::ERROR);
        return;
    }
    for(size_t i = 0; i < total; i++)
    {
        stu_data.scores[i].subject = peekString(msg, i * 3 + 2);
        stu_data.scores[i].score = atoi(peekString(msg, i * 3 + 3).c_str());
        stu_data.scores[i].examdate = peekString(msg, i * 3 + 4);
    }
}

StuConfigDB::StuConfigDB():DataBaseRW(LINESPERMAP_CONFIGDATA)
{

}

StuConfigDB::~StuConfigDB()
{

}

/**
 * @brief: 查询学生是否改变
 * @para: {void}
 * @return: bool
 **/
bool StuConfigDB::studentisReset()
{
    if(flag_stu_reset)
    {
        flag_stu_reset = false;
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * @brief: 返回学生在数据库文件中的map索引
 * @para: {void}
 * @return: int
 **/
int StuConfigDB::index()
{
    static int indexnum;
    if(studentisReset())
    {
        //找不到就放在最后
        indexnum = this->numofMap();
        for(int i = 0; i < this->numofMap(); i++)
        {
            if(readList(i, "id") == this->student_id)
            {
                indexnum = i;
                break;
            }
        }
    }
    return indexnum;
}

/**
 * @brief: 设置学生
 * @para: {stu_data}
 * @return: bool
 **/
bool StuConfigDB::setStudent(StuDataStruct stu_data)
{
    flag_stu_reset = true;
    student_id = stu_data.id;
    string openpath = "./data/config/" + stu_data.major + 
                      "/" + stu_data.grade + 
                      "/" + stu_data._class + ".yaml";
    LOG::logPrint("opening file: " + openpath, LOG::MSG);
    YamlHandler::loadFile(openpath);
    return true;
}

/**
 * @brief: 对学生信息数据库进行标签读操作
 * @para: {lable}
 * @return: string
 **/
string StuConfigDB::readLable(string lable)
{
    string res;
    res = YamlHandler::readList(index(), lable);
    return res;
}

/**
 * @brief: 对学生信息数据库进行标签写操作
 * @para: {lable, content}
 * @return: bool
 **/
bool StuConfigDB::writeLable(string lable, string content)
{
    YamlHandler::writeList(index(), lable, content);
    return true;
}

/**
 * @brief: 从学生信息数据库读取所有信息保存在数据结构中
 * @para: {lable}
 * @return: string
 **/
bool StuConfigDB::loadData(StuDataStruct &stu_data)
{
    LOG::logPrint("reading student data", LOG::MSG);
    stu_data.sex = this->readLable("sex");
    stu_data.phone = this->readLable("phone");
    stu_data.email = this->readLable("email");
    stu_data.birthday = this->readLable("birthday");
    stu_data.pid = this->readLable("pid");
    stu_data.address = this->readLable("address");
    stu_data.remark = this->readLable("remark");
    return true;
}

/**
 * @brief: 从数据库移除当前学生
 * @para: {void}
 * @return: bool
 **/
bool StuConfigDB::removeStudent()
{
    YamlHandler::deleteOneMap(this->index());
    return true;
}

StuScoreDB::StuScoreDB():DataBaseRW(LINESPERMAP_SCORESDATA)
{

}

StuScoreDB::~StuScoreDB()
{

}

/**
 * @brief: 返回科目对应的map索引
 * @para: {subject}
 * @return: int
 **/
int StuScoreDB::index(string subject)
{
    for(size_t i = 0; i < YamlHandler::linesPerMap(); i++)
    {
        if(YamlHandler::readList(i, "subject") == subject)
            return i;
    }
    return YamlHandler::linesPerMap();
}

/**
 * @brief: 设置学生
 * @para: {stu_data}
 * @return: bool
 **/
bool StuScoreDB::setStudent(StuDataStruct stu_data)
{
    string openpath = "./data/scores/" + stu_data.major + 
                      "/" + stu_data.grade + 
                      "/" + stu_data._class +
                      "/" + stu_data.id + ".yaml";
    LOG::logPrint("opening file: " + openpath, LOG::MSG);
    YamlHandler::loadFile(openpath);
    //打开班级数据文件
    p_subject_db.setClass(stu_data.major, stu_data.grade, stu_data._class);
    if(YamlHandler::numofMap() != p_subject_db.getTotalSubjects().size())
    {
        //如果科目数不对
        fillNewDataFile();
    }
    return true;
}

/**
 * @brief: 读取某个map中的某个标签
 * @para: {lable 注意将科目和信息压缩输入}
 * @return: string
 **/
string StuScoreDB::readLable(string lable)
{
    string res;
    res = YamlHandler::readList(index(peekString(lable, 0)), peekString(lable, 1));
    return res;
}

/**
 * @brief: 写入某个map中的某个标签
 * @para: {lable 注意将科目和标签压缩输入, content}
 * @return: string
 **/
bool StuScoreDB::writeLable(string lable, string content)
{
    YamlHandler::writeList(index(peekString(lable, 0)), peekString(lable, 1), content);
    return true;
}

/**
 * @brief: 获取某一科目的结果
 * @para: {subject}
 * @return: SubjectScore
 **/
SubjectScore StuScoreDB::readSubjectData(string subject)
{
    SubjectScore res;
    res.subject = subject;
    res.score = atoi(this->readLable(subject + " " + "score").c_str());
    return res;
}

/**
 * @brief: 获取所有科目的结果
 * @para: {subject}
 * @return: SubjectScore
 **/
bool StuScoreDB::loadData(StuDataStruct &stu_data)
{
    //获取所有科目
    stu_data.scores = p_subject_db.getTotalSubjects();
    LOG::logPrint("loading subject msg, total: " + std::to_string(stu_data.scores.size()), LOG::MSG);
    for(size_t i = 0; i < stu_data.scores.size(); i++)
    {
        //从数据库提取分数
        stu_data.scores[i].score = this->readSubjectData(stu_data.scores[i].subject).score;
    }
    return true;
}

/**
 * @brief: 确定考试科目 尚未录入分数
 * @para: {void}
 * @return: bool
 **/
bool StuScoreDB::fillNewDataFile()
{
    std::vector<SubjectScore> subject_vector = p_subject_db.getTotalSubjects();
    for(size_t i = 0; i < subject_vector.size(); i++)
    {
        //录入科目和成绩 成绩为-1 表示尚未录入
        YamlHandler::writeList(i, "subject", subject_vector[i].subject);
        YamlHandler::writeList(i, "score", std::to_string(-1));
    }
    LOG::logPrint("fill new score file", LOG::MSG);
}

/**
 * @brief: 
 * @para: {subject}
 * @return: SubjectScore
 **/

bool StuScoreDB::removeStudent()
{
    if(std::remove(this->currentPath().c_str()) == 0)
        return true;
    else{
        LOG::logPrint("remove failed", LOG::ERROR);
    }
}

Student::Student(string id)
{
    this->p_stu_data.id = id;
    if(this->isExist())
    {
        p_config_db.setStudent(this->p_stu_data);
        p_scores_db.setStudent(this->p_stu_data);
        p_config_db.loadData(this->p_stu_data);
        p_scores_db.loadData(this->p_stu_data);
    }
    else{
        LOG::logPrint("no such student", LOG::ERROR);
    }
}

Student::Student(StuDataStruct stu_data)
{
    resetStudent(stu_data);
}

/**
 * @brief: 获取学生信息数据结构
 * @para: {void}
 * @return: StuDataStruct
 **/
StuDataStruct Student::getStuData() const
{
    return this->p_stu_data;
}

bool Student::resetStudent(StuDataStruct stu_data)
{
    this->p_stu_data = stu_data;
    if(this->isExist())
    {
        p_config_db.setStudent(this->p_stu_data);
        p_scores_db.setStudent(this->p_stu_data);
        p_config_db.loadData(this->p_stu_data);
        p_scores_db.loadData(this->p_stu_data);
    }
    else{
        newStudent(stu_data);
    }
    return true;
}

/**
 * @brief: 更新学生信息数据库
 * @para: {void}
 * @return: StuDataStruct
 **/
bool Student::updateConfig(StuDataStruct stu_data)
{
    if(stu_data == this->p_stu_data)
        return false;
    if( stu_data._class != this->p_stu_data._class || 
        stu_data.grade != this->p_stu_data.grade  ||
        stu_data.major != this->p_stu_data.major
    )
    {
        //将学生从原有班级移除
        this->p_config_db.removeStudent();
        this->p_scores_db.removeStudent();
        LOG::logPrint("remove student from current class", LOG::WARNING);
        //重新打开班级数据库
        p_config_db.setStudent(stu_data);
        p_scores_db.setStudent(stu_data);
    }
    this->p_config_db.writeLable("name", stu_data.name);
    this->p_config_db.writeLable("id", stu_data.id);
    this->p_config_db.writeLable("phone", stu_data.phone);
    this->p_config_db.writeLable("email", stu_data.email);
    this->p_config_db.writeLable("sex", stu_data.sex);
    this->p_config_db.writeLable("birthday", stu_data.birthday);
    this->p_config_db.writeLable("pid", stu_data.pid);
    this->p_config_db.writeLable("address", stu_data.address);
    this->p_config_db.writeLable("remark", stu_data.remark);
    g_search_db.update(stu_data);
    this->p_stu_data = stu_data;
    LOG::logPrint("update student config data", LOG::MSG);
    return true;
}

/**
 * @brief: 更新学生成绩数据库
 * @para: {void}
 * @return: StuDataStructc
 **/
bool Student::updateScores(StuDataStruct stu_data)
{
    for(size_t i = 0; i < stu_data.scores.size(); i++)
    {
        string lable = stu_data.scores[i].subject + " score";
        this->p_scores_db.writeLable(lable, std::to_string(stu_data.scores[i].score));
    }
    return true;
}

/**
 * @brief: 添加新的学生
 * @para: {StuDataStruct}
 * @return: bool
 **/
bool Student::newStudent(StuDataStruct stu_data)
{
    stu_data.username = stu_data.id;
    stu_data.password = stu_data.id;
    this->p_stu_data = stu_data;
    //新建数据库文件
    this->p_config_db.setStudent(stu_data);
    this->p_scores_db.setStudent(stu_data);
    //写入查询表
    g_search_db.update(stu_data);
    //更新数据库
    this->p_config_db.writeLable("name", stu_data.name);
    this->p_config_db.writeLable("id", stu_data.id);
    this->p_config_db.writeLable("phone", stu_data.phone);
    this->p_config_db.writeLable("email", stu_data.email);
    this->p_config_db.writeLable("sex", stu_data.sex);
    this->p_config_db.writeLable("birthday", stu_data.birthday);
    this->p_config_db.writeLable("pid", stu_data.pid);
    this->p_config_db.writeLable("address", stu_data.address);
    this->p_config_db.writeLable("remark", stu_data.remark);
    g_search_db.update(stu_data);
    this->p_stu_data = stu_data;
    LOG::logPrint("add new student(id:" + stu_data.id + ")", LOG::MSG);
    return true;
}

/**
 * @brief: 查询该学生是否已经存在
 * @para: {void}
 * @return: bool
 **/
bool Student::isExist()
{
    if(g_search_db.searchStudent("id", p_stu_data.id, p_stu_data) >= 0)
        return true;
    else
        return false;
}

bool operator == (StuDataStruct a, StuDataStruct b)
{
    if(a.major == b.major)
    if(a.grade == b.grade)
    if(a._class == b._class)
    if(a.name == b.name)
    if(a.id == b.id)
    if(a.phone == b.phone)
    if(a.pid == b.pid)
    if(a.sex == b.sex)
    if(a.birthday == b.birthday)
    if(a.email == b.email)
    if(a.address == b.address)
    if(a.remark == b.remark)
    if(a.username == b.username)
    if(a.password == b.password)
        return true;
    return false;
}

Aclass::Aclass(string msg)
{
    this->major = peekString(msg, 1);
    this->grade = peekString(msg, 2);
    this->_class = peekString(msg, 3);
    YamlHandler file_handler(LINESPERMAP_CONFIGDATA);
    string path = "./data/config/" + this->major + "/" + this->grade + "/" +this->_class + ".yaml";
    file_handler.loadFile(path);
    for(size_t i = 0; i < totalStudent(); i++)
    {
        Student temp_student;
        StuDataStruct temp_stu_data;
        temp_stu_data.id = file_handler.readList(i, "id");
        temp_student.resetStudent(temp_stu_data);
        this->students.push_back(temp_student);
    }
}

/**
 * @brief: 获取班级人数
 * @para: {void}
 * @return: int
 **/
int Aclass::totalStudent()
{
    YamlHandler file_handler(LINESPERMAP_CONFIGDATA);
    if(this->_class == "")
        return -1;
    string path = "./data/config/" + this->major + "/" + this->grade + "/" +this->_class + ".yaml";
    file_handler.loadFile(path);
    return file_handler.numofMap();
}

/**
 * @brief: 获取科目人数
 * @para: {void}
 * @return: int
 **/
int Aclass::totalSubject()
{
    YamlHandler file_handler(4);
    if(this->_class == "")
        return -1;
    string path = "./data/scores/" + this->major + "/" + this->grade + "/" +this->_class + "/config.yaml";
    file_handler.loadFile(path);
    return file_handler.numofMap();
}

/**
 * @brief: 获取发送的字节流
 * @para: {void}
 * @return: int
 **/
string Aclass::getZipMsg(int index)
{
    StuDataStruct stu_data = this->students[index].getStuData();
    return DataBaseRW::zipScoreData(stu_data);
}

/**
 * @brief: 获取发送的字节流
 * @para: {void}
 * @return: int
 **/
string Aclass::getShortConfig(int index)
{
    string res;
    StuDataStruct stu_data = this->students[index].getStuData();
    res = stu_data.name + " " + stu_data.id;
    return res;
}
